//
//  main.c
//  USB——Monitor
//
//  Created by MrPanda on 2016/12/15.
//  Copyright © 2016年 MrPanda. All rights reserved.
//

#include <stdio.h>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/IOMessage.h>
#include <IOKit/IOCFPlugIn.h>
#include <IOKit/usb/IOUSBLib.h>

typedef struct MyPrivateData {
    io_object_t				notification;
    IOUSBDeviceInterface	**deviceInterface;
    CFStringRef				deviceName;
    UInt32					locationID;
    UInt16                  vendorID;
    UInt16                 productID;
} MyPrivateData;

static IONotificationPortRef	gNotifyPort;
static io_iterator_t			gAddedIter;
static CFRunLoopRef				gRunLoop;
#pragma mark --设备状态变更通知
void DeviceNotification(void *refCon, io_service_t service, natural_t messageType, void *messageArgument)
{
    kern_return_t	kr;
    MyPrivateData	*privateDataRef = (MyPrivateData *) refCon;
    
    if (messageType == kIOMessageServiceIsTerminated) {
        fprintf(stderr, "设备已拔出！\n");
        
        // Dump our private data to stderr just to see what it looks like.
        fprintf(stderr, "设备名称: ");
        CFShow(privateDataRef->deviceName);
        fprintf(stderr, "locationID: 0x%x.\n\n", (unsigned int)privateDataRef->locationID);
        CFRelease(privateDataRef->deviceName);
        fprintf(stderr, "厂家ID: 0x%x.\n\n", (unsigned int)privateDataRef->vendorID);
        fprintf(stderr, "产品ID: 0x%x.\n\n", (unsigned int)privateDataRef->productID);
        
        if (privateDataRef->deviceInterface) {
            kr = (*privateDataRef->deviceInterface)->Release(privateDataRef->deviceInterface);
        }
        
        kr = IOObjectRelease(privateDataRef->notification);
        
        free(privateDataRef);
    }
}

#pragma mark--新加设备调用
void DeviceAdded(void *refCon, io_iterator_t iterator)
{
    kern_return_t		kr;
    io_service_t		usbDevice;
    IOCFPlugInInterface	**plugInInterface = NULL;
    SInt32				score;
    HRESULT 			res;
    
    while ((usbDevice = IOIteratorNext(iterator))) {
        io_name_t		deviceName;
        CFStringRef		deviceNameAsCFString;
        MyPrivateData	*privateDataRef = NULL;
        UInt32			locationID;
        UInt16          vendorID;
        UInt16          productID;
        printf("新增设备\n");
        
        // Add some app-specific information about this device.
        // Create a buffer to hold the data.
        privateDataRef = malloc(sizeof(MyPrivateData));
        bzero(privateDataRef, sizeof(MyPrivateData));
        
        // 获得设备名称.
        kr = IORegistryEntryGetName(usbDevice, deviceName);
        if (KERN_SUCCESS != kr) {
            deviceName[0] = '\0';
        }
        
        deviceNameAsCFString = CFStringCreateWithCString(kCFAllocatorDefault, deviceName,
                                                         kCFStringEncodingASCII);
        
        // Dump our data to stderr just to see what it looks like.
        fprintf(stderr, "设备名称: ");
        CFShow(deviceNameAsCFString);
        privateDataRef->deviceName = deviceNameAsCFString;
        
        // Now, get the locationID of this device. In order to do this, we need to create an IOUSBDeviceInterface
        // for our device. This will create the necessary connections between our userland application and the
        // kernel object for the USB Device.
        kr = IOCreatePlugInInterfaceForService(usbDevice, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID,
                                               &plugInInterface, &score);
        
        if ((kIOReturnSuccess != kr) || !plugInInterface) {
            fprintf(stderr, "IOCreatePlugInInterfaceForService returned 0x%08x.\n", kr);
            continue;
        }
        
        // Use the plugin interface to retrieve the device interface.
        res = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID),
                                                 (LPVOID*) &privateDataRef->deviceInterface);
        
        // Now done with the plugin interface.
        (*plugInInterface)->Release(plugInInterface);
        
        if (res || privateDataRef->deviceInterface == NULL) {
            fprintf(stderr, "QueryInterface returned %d.\n", (int) res);
            continue;
        }
        
        
        //获取设备供应商ID（）
        kr = (*privateDataRef->deviceInterface)->GetDeviceVendor(privateDataRef->deviceInterface,&vendorID);
        if (KERN_SUCCESS != kr) {
            fprintf(stderr, "GetVendorID returned 0x%08x.\n", kr);
            continue;
        }
        else {
            fprintf(stderr, "厂家ID: 0x%x\n\n", (unsigned int)vendorID);
        }
        
        privateDataRef->vendorID = vendorID;
        
        //获取设备产品ID（）
        kr = (*privateDataRef->deviceInterface)->GetDeviceProduct(privateDataRef->deviceInterface,&productID);
        if (KERN_SUCCESS != kr) {
            fprintf(stderr, "GetproductID returned 0x%08x.\n", kr);
            continue;
        }
        else {
            fprintf(stderr, "产品ID: 0x%x\n\n", (unsigned int)productID);
        }
        
        privateDataRef->productID = productID;
        
        //获取位置ID（位置ID和USB设备无关，不同的设备插入相同的USB接口，位置ID相同）。
        kr = (*privateDataRef->deviceInterface)->GetLocationID(privateDataRef->deviceInterface, &locationID);
        if (KERN_SUCCESS != kr) {
            fprintf(stderr, "GetLocationID returned 0x%08x.\n", kr);
            continue;
        }
        else {
            fprintf(stderr, "Location ID: 0x%x\n\n", (unsigned int)locationID);
        }
        
        privateDataRef->locationID = locationID;
        
        kr = IOServiceAddInterestNotification(gNotifyPort,						// notifyPort
                                              usbDevice,						// service
                                              kIOGeneralInterest,				// interestType
                                              DeviceNotification,				// callback
                                              privateDataRef,					// refCon
                                              &(privateDataRef->notification)	// notification
                                              );
        
        if (KERN_SUCCESS != kr) {
            printf("IOServiceAddInterestNotification returned 0x%08x.\n", kr);
        }
        
        // Done with this USB device; release the reference added by IOIteratorNext
        kr = IOObjectRelease(usbDevice);
    }
}

//信号处理
#pragma mark --信号处理
void SignalHandler(int sigraised){
    fprintf(stderr, "中断\n");
    
    exit(0);
}

#pragma mark--MAIN函数
/**
 main函数
 
 @param argc int
 @param argv 字符数组
 @return 返回值int型，返回之后程序结束
 */
int main(int argc, const char * argv[]) {
    CFMutableDictionaryRef matchingDict;//匹配字典
    CFRunLoopSourceRef    runLoopSource;//运行循环源对象的引用
    //    CFNumberRef           numberRef;
    kern_return_t         kr;
    sig_t                 oldHandler;
    //设置一个信号处理，当我们从命令行中断的时候可以清理。否则永远开启运行循环
    oldHandler = signal(SIGINT, SignalHandler);
    matchingDict = IOServiceMatching(kIOUSBDeviceClassName);
    /**
     *创建一个通知端口，并把它的运行循环事件源添加到当前运行循环上
     */
    gNotifyPort = IONotificationPortCreate(kIOMasterPortDefault);
    runLoopSource = IONotificationPortGetRunLoopSource(gNotifyPort);
    gRunLoop = CFRunLoopGetCurrent();
    CFRunLoopAddSource(gRunLoop, runLoopSource, kCFRunLoopDefaultMode);
    kr = IOServiceAddMatchingNotification(gNotifyPort, kIOFirstMatchNotification, matchingDict, DeviceAdded, NULL, &gAddedIter);
    //    迭代一次已有设备和部门通知
    DeviceAdded(NULL, gAddedIter);
    //    开启运行循环
    fprintf(stderr, "Starting run loop.\n\n");
    CFRunLoopRun();
    
    // We should never get here
    fprintf(stderr, "Unexpectedly back from CFRunLoopRun()!\n");
    return 0;
}
